//! Анимация
class Animation{

friend class Object;

protected:

	//! Структура джоинта (отвечает за деформацию кости)
	struct Joint{

		//! Ключ анимации
		struct KeyComparator{
			bool operator ()(const float a, const float b){
				return a<b;
			}
		};

		//! Тип для карты хранящей массив ключевых кадров для элемента джоинта (например позиции)
		typedef map <float,vec3,KeyComparator> TranslateKey;
		typedef map <float,quat,KeyComparator> RotateKey;

		//! Матрица первичной деформации
		mat4 relative_matrix;
		
		//! Сдвиг
		vec3 translate;
		
		//! Поворот
		quat rotate;
		
		//! Предок
		short parent;
		
		//! Ключи смещения
		TranslateKey translate_key;

		//! Ключи поворота
		RotateKey rotate_key;

	};
	
	//! Тип для массива джоинтов
	typedef vector <Joint> JointVector;
	
	//! Массив джоинтов
	JointVector _joint;
	
	//! Длинна анимации
	float _length;
	
	//! Скорость анимации
	float _speed;
	
public:

	//! Тип для массива финальных матрицы
	typedef vector <mat4> FinalMatrix;
	
	//! Массив финальных матриц
	FinalMatrix final_matrix;
	
protected:

	//! Флаг обновления
	bool _update;
	
	//! Обновит финальные матрицы
	void _update_final_matrix(){
		if (final_matrix.size()!=_joint.size())
			final_matrix.resize(_joint.size());
		for (unsigned int i = 0; i < _joint.size(); i++){
			final_matrix[i] = toMat4(_joint[i].rotate);
			final_matrix[i][3][0] = _joint[i].translate.x;
			final_matrix[i][3][1] = _joint[i].translate.y;
			final_matrix[i][3][2] = _joint[i].translate.z;
			final_matrix[i] = _joint[i].relative_matrix * final_matrix[i];
			if (_joint[i].parent >= 0)
				final_matrix[i] = final_matrix[_joint[i].parent] * final_matrix[i];
		}	
		_update = false;
	}
	
public:

	//! Задает число джоинтов в анимации
	void resize(short n){
		_update = true;
		if (n<0) n=0;
		_joint.resize(n);
	}

	//! Число джоинтов в анимации
	const short joint_count() const{
		return _joint.size();
	}

	//! Доступ к джоинту константный
	const Joint& joint(short n) const{
		return _joint[n];
	}

	//! Доступ чтение запись, активирует обновление анимации
	Joint& joint(short n){
		_update = true;
		return _joint[n];
	}
	
	//! Вернет длинну анимации в секундах
	const float length() const{
		return _length;
	}
	
	//! Установит длину анимации
	void set_length(float len){
		_update = true;
		_length = len>=0?len:0;
	}
	
	//! Вернет скорость воспроизведения
	const float speed() const{
		return _speed;
	}

	//! Установка скорости воспроизведения
	void set_speed(float spd){
		_update = true;
		_speed = spd;
	}
	
	//! Очистка
	void clear(){
		_update = true;
		_joint.clear();
		_length = 0;
		_speed = 1;	
		_from = 0;
		_to = 0;
		_plength = 0;
		_timer = 0;
		_repeat = true;
		FinalMatrix().swap(final_matrix); // final_matrix.clear(); + free memory
	}
	
	
protected:

	float _position;
	
	float _from;
	float _to;
	float _plength;
	float _timer;
	bool _repeat;
	
public:

	//! Вернет true если вопроизведение звершилось
	bool finished(){
		return ((System::device().timer.seconds - _timer) * _speed) >= _to;
	}

	//! Установка позиции в соотвествии с анимацией заданной set_play(...
	//* Вернет true если анимация завершилась и _repeat отключен
	//* Расчет ведется от состояния текущего таймера и скорости
	bool play(){
		float pos = (System::device().timer.seconds - _timer) * _speed;
		if (_repeat)
			set_position(_from + pos - (floor(pos/_plength)*_plength));
		else 
			if (_from + pos >= _to)
				return true;
			else 
				set_position(_from + pos);
		return false;
	}

	//! Установка воспроизведения анимации
	void set_play(float from, float to, bool repeat){
		if (from<0) from = 0;
		if (to<0) to = 0;
		_timer = System::device().timer.seconds;
		_repeat = repeat;
		if (from<=to) {
			_from = from;
			_to = to;
		} else {
			_to = from;
			_from = to;
		}
		_plength = _to - _from;
	}

	//! Установит позицию анимации, вернет true если анимация завершилась
	void set_position(float time){
		_position = time;
		if (time>_length)
			time = _length;
		else if (time<0)
			time = 0;
		_update = true;
		// Цикл по джоинтам
		for (JointVector::iterator jit = _joint.begin();jit!=_joint.end();++jit){

			float t;

			if (jit->rotate_key.empty()){
				jit->rotate = quat(1,0,0,0);
			} else {
				// Поворот
				Joint::RotateKey::reverse_iterator r0,r1;
				r1 = jit->rotate_key.rend();
				for (r0 = jit->rotate_key.rbegin();r0 != jit->rotate_key.rend(); ++r0){
					if (time >= (*r0).first){
						r1 = r0;
						r1--;
						break;
					}
				}
				quat q0,q1;
				if (r1 == jit->rotate_key.rend()) {
					if (r0 == jit->rotate_key.rend()){
						// до первого ключа 0 -> r0
						t = (1.0f / jit->rotate_key.begin()->first) * time;
						q0 = quat(1,0,0,0);
						q1 = jit->rotate_key.begin()->second;	
					} else {
						// после последнего ключа r0 <- 0
						t = (1.0f / (_length - (*r0).first)) * (time - (*r0).first);
						q0 = (*r0).second;
						q1 = quat(1,0,0,0);
					}
				} else {
					// между r0 и r1
					t = (1.0f / ((*r1).first - (*r0).first)) * (time - (*r0).first);
					q0 = (*r0).second;
					q1 = (*r1).second;
				}
				jit->rotate = slerp(q0,q1,t);
			}
			
			// Сдвиг
			if (jit->translate_key.empty()){
				jit->translate = vec3(0,0,0);
			} else {
				Joint::TranslateKey::reverse_iterator p0,p1;
				p1 = jit->translate_key.rend();
				for (p0 = jit->translate_key.rbegin();p0 != jit->translate_key.rend(); ++p0){
					if (time >= (*p0).first){
						p1 = p0;
						p1--;
						break;
					}
				}
				if (p1 == jit->translate_key.rend()) {
					if (p0 == jit->translate_key.rend()) {
						// до первого ключа p0 <- 0
						t = (1.0f / jit->translate_key.begin()->first) * time;
						jit->translate = glm::mix(jit->translate_key.begin()->second,vec3(0.0f),t);
					} else {
						// после последнего ключа p0 <- 0
						t = (1.0f / (_length - (*p0).first)) * (time - (*p0).first);
						jit->translate = glm::mix(vec3(0.0f),(*p0).second,t);
					}
				} else {
					// между p0 и p1
					t = (1.0f / ((*p1).first - (*p0).first)) * (time - (*p0).first);
					jit->translate = mix((*p0).second,(*p1).second,t);
				}
			}
		}
	}
	
public: 

	Animation(){
		_update = true;
		_speed = 1;
		_length = 0;
		_position = 0;
		_from = 0;
		_to = 0;
		_plength = 0;
		_timer = 0;
		_repeat = true;
	}

};

